extern crate image;

use common_uu::IResult;
use image::{open, GenericImageView, Rgba};

//可调节参数（当前参数即有很高的识别率故不作为参数接收，仅在识别出错时调整）
//通常情况下调节优先顺序为从上至下，即首先排查扫描幅度，没效果再依次往下，通常情况
//为控制变量当调节某一参数时应保证其它参数恢复为初始值


/**
扫描幅度SCAN_RANGE（向上调整； 调整幅度不大于1）
！！该参数会严重影响识别效率           时间增量 △t = 186 * △SCAN_RANGE   (ms)
 */
const SCAN_RANGE: usize = 3;

/**
非同色线不同色权重NOT_SIMILAR_LINE_NOT_SIMILAR_WEIGHT（向上或向下调整； 调整幅度不大于5）
 */
const NOT_SIMILAR_LINE_NOT_SIMILAR_WEIGHT: u32 = 40;

/**
任意线同色权重LINE_SIMILAR_WEIGHT（向上或向下调整； 调整幅度不大于5）
 */
const LINE_SIMILAR_WEIGHT: u32 = 20;

/**
同色线不同色权重SIMILAR_LINE_NOT_SIMILAR_WEIGHT（向上或向下调整； 调整幅度不大于5）
 */
const SIMILAR_LINE_NOT_SIMILAR_WEIGHT: u32 = 50;

/**
任意线不同色权重LINE_NOT_SIMILAR_WEIGHT（向上或向下调整； 调整幅度不大于5）
 */
const LINE_NOT_SIMILAR_WEIGHT: u32 = 60;

/**
任意线相似权重LINE_WEIGHT（向上或向下调整； 调整幅度不大于5）
 */
const LINE_WEIGHT: u32 = 40;

/**
同色线相似权重SIMILAR_LINE_WEIGHT（向上或向下调整； 调整幅度不大于2）
 */
const SIMILAR_LINE_WEIGHT: u32 = 10;

/**
同色线同色权重SIMILAR_LINE_SIMILAR_WEIGHT（向上或向下调整； 调整幅度不大于1）
 */
const SIMILAR_LINE_SIMILAR_WEIGHT: u32 = 0;

/**
非同色线同色权重NOT_SIMILAR_LINE_SIMILAR_WEIGHT（向上或向下调整； 调整幅度不大于1）
 */
const NOT_SIMILAR_LINE_SIMILAR_WEIGHT: u32 = 0;

/**
非同色线相似权重NOT_SIMILAR_LINE_WEIGHT（向上或向下调整； 调整幅度不大于1）
 */
const NOT_SIMILAR_LINE_WEIGHT: u32 = 3;

/**
不相似标准NOT_SIMILAR_RANK（向上或向下调整； 调整幅度不大于5）
 */
const NOT_SIMILAR_RANK: f64 = 80_f64;

/**
相似标准SIMILAR_RANK（向上或向下调整； 调整幅度不大于0.5）
 */
const SIMILAR_RANK: f64 = 10_f64;


pub fn run(picpath: String, blockpath: String) -> IResult<f64> {
    //根据滑块图片计算得出Y轴坐标
    let location_y = find_y_formblock(blockpath)? as i32;
    let pic = open(picpath)?;
    let width = pic.width();
    let mut weights = [0; 420 + 840 * SCAN_RANGE];
    let mut index_of_weigth = 0;
    let mut lines = vec![vec![Rgba([0_u8; 4]); 21]; 6];

    #[allow(unused_assignments)]
        let mut index_y = 0;
    #[allow(unused_assignments)]
        let mut index_x = 0;

    for y in location_y - SCAN_RANGE as i32..=location_y + SCAN_RANGE as i32 {
        for i in 30..(pic.width() as i32) - 30 {
            index_y = 0;
            for m in (y - 33)..=(y - 28) {
                index_x = 0;
                for n in (i - 30)..=(i - 10) {
                    if m < 0 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_x += 1;
                }
                index_y += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_y = 0;
            for m in (y - 33)..=(y - 28) {
                index_x = 0;
                for n in (i + 10)..=(i + 30) {
                    if m < 0 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_x += 1;
                }
                index_y += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_x = 0;
            for m in (y - 30)..=(y - 10) {
                index_y = 0;
                for n in (i + 28)..=(i + 33) {
                    if n >= pic.width() as i32 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_y += 1;
                }
                index_x += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_x = 0;
            for m in (y + 10)..=(y + 30) {
                index_y = 0;
                for n in (i + 28)..=(i + 33) {
                    if n >= pic.width() as i32 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_y += 1;
                }
                index_x += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_x = 0;
            for m in (y - 30)..=(y - 10) {
                index_y = 0;
                for n in (i - 33)..=(i - 28) {
                    if n < 0 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_y += 1;
                }
                index_x += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_x = 0;
            for m in (y + 10)..=(y + 30) {
                index_y = 0;
                for n in (i - 33)..=(i - 28) {
                    if n < 0 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_y += 1;
                }
                index_x += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_y = 0;
            for m in (y + 28)..=(y + 33) {
                index_x = 0;
                for n in (i - 30)..=(i - 10) {
                    if m >= pic.height() as i32 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_x += 1;
                }
                index_y += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_y = 0;
            for m in (y + 28)..=(y + 33) {
                index_x = 0;
                for n in (i + 10)..=(i + 30) {
                    if m >= pic.height() as i32 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_x += 1;
                }
                index_y += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_of_weigth += 1;
        }
    }
    let mut maxweight: u32 = 0;
    let mut index = 0;
    for p in 0..weights.len() {
        if weights[p] > maxweight {
            maxweight = weights[p];
            index = p;
        }
    }
    let x = (index % 420) as f64;

    //缺口中心的x坐标
    let _location_x = index % 420 + 30;
    
    let x2 = x / width as f64;
    println!("move position (480img): {}px, scale: {}", x, x2);
    Ok(x2)
}

//根据滑块图片确定Y坐标
fn find_y_formblock(block_path: String) -> IResult<u32> {
    let blockpic = image::open(block_path)?;
    let mut up: u32 = 0;
    let mut down: u32 = 0;
    let mut upp: bool = false;
    let mut downp: bool = false;
    for y in 0..blockpic.height() {
        //遍历滑块的纵坐标
        if !upp {
            for x in 0..blockpic.width() {
                //遍历滑块的横坐标
                let pixel = blockpic.get_pixel(x, y);
                if pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0 {
                    up = y;
                    upp = true;
                    break;
                }
            }
        }
        if !downp {
            for x in 0..blockpic.width() {
                //遍历滑块的横坐标
                let pixel = blockpic.get_pixel(x, blockpic.height() - y - 1);
                if pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0 {
                    down = blockpic.height() - y - 1;
                    downp = true;
                    break;
                }
            }
        }
        if upp && downp {
            break;
        }
    }
    let r = (down - up + 1) * 18 / 29 + up - 1;
    Ok(r)
}

type RgbaMy = Rgba<u8>;

//根据6条线得到对应的权值
fn get_weight_bylines(lines: &Vec<Vec<RgbaMy>>) -> u32 {
    let mut result_weigtht: u32 = 0;
    for lineindex in 0..lines.len() - 1 {
        if lines[lineindex][0][0] == 255 || lines[lineindex + 1][0][0] == 255 {
            continue;
        }
        let temp = not_similar_between_twolines(&lines[lineindex], &lines[lineindex + 1]);
        if temp > result_weigtht {
            result_weigtht = temp;
        }
    }
    result_weigtht
}

//得到两个线条的差异值
fn not_similar_between_twolines(line: &Vec<RgbaMy>, line_1: &Vec<RgbaMy>) -> u32 {
    return if is_similar_rgbline(line) && is_similar_rgbline(line_1) {
        if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1))
            < SIMILAR_RANK
        {
            SIMILAR_LINE_SIMILAR_WEIGHT
        } else if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1))
            > NOT_SIMILAR_RANK
        {
            SIMILAR_LINE_NOT_SIMILAR_WEIGHT
        } else {
            SIMILAR_LINE_WEIGHT
        }
    } else if (!is_similar_rgbline(line)) && (!is_similar_rgbline(line_1)) {
        if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1)) < SIMILAR_RANK {
            NOT_SIMILAR_LINE_SIMILAR_WEIGHT
        } else if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1)) > NOT_SIMILAR_RANK {
            NOT_SIMILAR_LINE_NOT_SIMILAR_WEIGHT
        } else {
            NOT_SIMILAR_LINE_WEIGHT
        }
    } else {
        if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1)) < SIMILAR_RANK {
            LINE_SIMILAR_WEIGHT
        } else if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1)) > NOT_SIMILAR_RANK {
            LINE_NOT_SIMILAR_WEIGHT
        } else {
            LINE_WEIGHT
        }
    };
}

//判断是否是同色素线条
fn is_similar_rgbline(line: &Vec<RgbaMy>) -> bool {
    let mut arr_r = [0; 21];
    let mut arr_g = [0; 21];
    let mut arr_b = [0; 21];
    for i in 0..21 {
        arr_r[i] = line[i][0];
        arr_g[i] = line[i][1];
        arr_b[i] = line[i][2];
    }
    if get_variance_fromarr(&arr_r) > SIMILAR_RANK {
        return false;
    }
    if get_variance_fromarr(&arr_g) > SIMILAR_RANK {
        return false;
    }
    if get_variance_fromarr(&arr_b) > SIMILAR_RANK {
        return false;
    }
    true
}

//得到线条的平均色素
fn getaveragecolor(line: &Vec<RgbaMy>) -> RgbaMy {
    let mut sumr: u32 = 0;
    let mut sumg: u32 = 0;
    let mut sumb: u32 = 0;
    for color in line {
        sumr += color[0] as u32;
        sumg += color[1] as u32;
        sumb += color[2] as u32;
    }
    Rgba([
        (sumr / line.len() as u32) as u8,
        (sumg / line.len() as u32) as u8,
        (sumb / line.len() as u32) as u8,
        0_u8,
    ])
}

//得到一组数据的方差
fn get_variance_fromarr(arr: &[u8; 21]) -> f64 {
    let mut sum: u32 = 0;
    for val in arr {
        sum += *val as u32;
    }
    let average: f64 = sum as f64 / arr.len() as f64;
    let mut sum: f64 = 0f64;
    for val in arr {
        sum += (*val as f64 - average) * (*val as f64 - average);
    }
    (sum / arr.len() as f64).sqrt()
}

//得到两个像素的色差
fn get_distance_bycolor(color: RgbaMy, color_1: RgbaMy) -> f64 {
    ((color_1[0] as f64 - color[0] as f64) * (color_1[0] as f64 - color[0] as f64)
        + (color_1[1] as f64 - color[1] as f64) * (color_1[1] as f64 - color[1] as f64)
        + (color_1[2] as f64 - color[2] as f64) * (color_1[2] as f64 - color[2] as f64))
        .sqrt()
}
